package io.activej.serializer;

import io.activej.serializer.annotations.Deserialize;
import io.activej.serializer.annotations.Serialize;
import io.activej.serializer.def.SimpleSerializerDef;
import org.junit.Test;

import static io.activej.serializer.Utils.DEFINING_CLASS_LOADER;
import static io.activej.serializer.Utils.doTest;
import static org.junit.Assert.assertEquals;

public class SimpleSerializerDefTest {

	@Test
	public void test() {
		BinarySerializer<ExternalClass> serializer = SerializerFactory.builder()
			.with(ExternalComponent.class, ctx -> new SimpleSerializerDef<ExternalComponent>() {
				@Override
				protected BinarySerializer<ExternalComponent> createSerializer(int version, CompatibilityLevel compatibilityLevel) {
					return new BinarySerializer<>() {
						@Override
						public void encode(BinaryOutput out, ExternalComponent item) {
							out.writeVarInt(item.getX());
							out.writeUTF8(item.getY());
						}

						@Override
						public ExternalComponent decode(BinaryInput in) throws CorruptedDataException {
							int x = in.readVarInt();
							String y = in.readUTF8();

							return new ExternalComponent(x, y);
						}
					};
				}
			})
			.build()
			.create(DEFINING_CLASS_LOADER, ExternalClass.class);

		ExternalClass original = new ExternalClass(
			"test",
			new ExternalComponent(
				123,
				"inner test 1"
			),
			new ExternalComponent(
				456,
				"inner test 2"
			)
		);

		ExternalClass copy = doTest(original, serializer);

		assertEquals(original, copy);
	}

	public static final class ExternalClass {
		private final String name;
		private final ExternalComponent component1;
		private final ExternalComponent component2;

		public ExternalClass(@Deserialize("name") String name, @Deserialize("component1") ExternalComponent component1, @Deserialize("component2") ExternalComponent component2) {
			this.name = name;
			this.component1 = component1;
			this.component2 = component2;
		}

		@Serialize
		public String getName() {
			return name;
		}

		@Serialize
		public ExternalComponent getComponent1() {
			return component1;
		}

		@Serialize
		public ExternalComponent getComponent2() {
			return component2;
		}

		@Override
		public boolean equals(Object o) {
			if (this == o) return true;
			if (o == null || getClass() != o.getClass()) return false;

			ExternalClass that = (ExternalClass) o;

			if (!name.equals(that.name)) return false;
			if (!component1.equals(that.component1)) return false;
			return component2.equals(that.component2);
		}

		@Override
		public int hashCode() {
			int result = name.hashCode();
			result = 31 * result + component1.hashCode();
			result = 31 * result + component2.hashCode();
			return result;
		}
	}

	public static final class ExternalComponent {
		private final int x;
		private final String y;

		public ExternalComponent(int x, String y) {
			this.x = x;
			this.y = y;
		}

		public int getX() {
			return x;
		}

		public String getY() {
			return y;
		}

		@Override
		public boolean equals(Object o) {
			if (this == o) return true;
			if (o == null || getClass() != o.getClass()) return false;

			ExternalComponent that = (ExternalComponent) o;

			if (x != that.x) return false;
			return y.equals(that.y);
		}

		@Override
		public int hashCode() {
			int result = x;
			result = 31 * result + y.hashCode();
			return result;
		}
	}
}
